#include "pch.h"
#include "TransCoder.h"
#include "File.h"



TransCoder::TransCoder(std::wstring _qsvPath, std::wstring _outputPath) {
    qsvPath = _qsvPath;
    outputPath = _outputPath + L"\\";
    qsv = std::ifstream(qsvPath, std::ios::binary);
    if (!qsv.is_open()) {
        throw std::exception("unable open");
    }
    outputName = File::Basename(qsvPath);
    if (!File::Exist(outputPath)) {
        File::CreateDeepDirectory(outputPath);
    }
    //File::TempPath();
    TempFilePath(File::TempPath() + outputName + L".temp");
    if (File::Exist(TempFilePath())) {
        File::Delete(TempFilePath());
    }
    temp = std::ofstream(TempFilePath(), std::ios::binary);
}

void TransCoder::Transcode() {
    SeekBegin();
    SkipMeta();
    ::SendMessage(hDlg, msg, 1, ProgressInterval() + Index());
    while (true) {
        try {
            if (ReadNext()) {
                auto buffer = GetTag();
                temp.write((char*)buffer.first, buffer.second);
                delete [] buffer.first;
            } else {
                SkipMeta();
                SeekNextTag();
                SeekNextTag();
            }
        } catch (std::exception& e) {
            if (e.what() == std::string("endOfStream")) {
                break;
            } else {
                throw;
            }
        }
    }
    ::SendMessage(hDlg, msg, 2, ProgressInterval() + Index());
    qsv.close();
    temp.close();
}

TransCoder::~TransCoder() {
}

bool TransCoder::CheckTAG() {
    Byte buffer[10] = { 0 };
    const char* tag = "QIYI VIDEO";
    qsv.seekg(0, std::ios_base::beg);
    qsv.read((char*)buffer, 10);
    return (strcmp(tag, (char*)buffer) == 0) ? true : false;
}

bool TransCoder::CheckVersion() {
    Byte buffer[4] = { 0 };
    qsv.seekg( 10L, std::ios_base::beg);
    qsv.read((char*)buffer, 4);
    return BytesToInt((char*)buffer, 0) == 2 ? true : false;
}

void TransCoder::SeekBegin() {
    long offset = 0L;
    int size = 0;
    Byte buffer[12] = { 0 };
    qsv.seekg( 74, std::ios_base::beg);
    qsv.read((char*)buffer, 12);
    offset = BytesToLong((char*)buffer, 0);
    size = BytesToInt((char*)buffer, 8);
    qsv.seekg(offset + size, std::ios_base::beg);
}

// len =3095
void TransCoder::SkipMeta() {
    qsv.seekg(0xD, std::ios_base::cur);
    int len = 0;
    while (true) {
        Byte bit =  0 ;
        qsv.read(&bit, 1);
        int num = *reinterpret_cast<unsigned char*>(&bit);
        ++len;
        if (num == 0x9) {
            Byte buffer[4] = { 0 };
            qsv.read((char*)buffer, 4);
            int size =
                (0xFF & buffer[0]) << 24 | (0xFF & buffer[1]) << 16 | (0xFF & buffer[2]) << 8 | (0xFF & buffer[3]);
            if (len == size)
                break;
            else {
                qsv.seekg(-4, std::ios_base::cur);
            }
        }
    }
}

void TransCoder::SeekNextTag() {
    int dataSize = 0;
    Byte buffer[4] = { 0 };
    qsv.read((char*)buffer, 4);
    dataSize = (0xFF & buffer[1]) << 16 | (0xFF & buffer[2]) << 8 | (0xFF & buffer[3]);
    qsv.seekg(dataSize + 11, std::ios_base::cur);
}

std::pair<Byte*, int> TransCoder::GetTag() {
    int dataSize = 0;
    Byte buffer[4] = { 0 };
    qsv.read((char*)buffer, 4);
    qsv.seekg(-4, std::ios_base::cur);
    dataSize = (0xFF & buffer[1]) << 16 | (0xFF & buffer[2]) << 8 | (0xFF & buffer[3]);
    Byte* tag = new Byte[15 + dataSize];
    qsv.read((char*)tag, 15 + dataSize);
    std::pair<Byte*, int> buf(tag, 15 + dataSize);
    return buf;
}

bool TransCoder::ReadNext() {

    if (qsv.tellg() >= (FlvWriter::GetStreamLength(qsv) - 11)) {
        throw std::exception("endOfStream");
    }
    Byte buffer[11] = { 0 };
    qsv.read((char*)buffer, 11);
    qsv.seekg(-11, std::ios_base::cur);
    if ((buffer[8] | buffer[9] | buffer[10]) == 0 && (buffer[0] == 0x8 || buffer[0] == 0x9)) {
        return true;
    } else {
        return false;
    }
}

int TransCoder::BytesToInt(char* paramArrayOfByte, int paramInt) {
    return 0xFF & paramArrayOfByte[paramInt] | (0xFF & paramArrayOfByte[(paramInt + 1)]) << 8 | (0xFF & paramArrayOfByte[(paramInt + 2)]) << 16 | (0xFF & paramArrayOfByte[(paramInt + 3)]) << 24;
}

long TransCoder::BytesToLong(char* paramArrayOfByte, int paramInt) {
    int i = 0xFF & paramArrayOfByte[paramInt] | (0xFF & paramArrayOfByte[(paramInt + 1)]) << 8 | (0xFF & paramArrayOfByte[(paramInt + 2)]) << 16 | (0xFF & paramArrayOfByte[(paramInt + 3)]) << 24;
    int j = 0xFF & paramArrayOfByte[(paramInt + 4)] | (0xFF & paramArrayOfByte[(paramInt + 5)]) << 8 | (0xFF & paramArrayOfByte[(paramInt + 6)]) << 16 | (0xFF & paramArrayOfByte[(paramInt + 7)]) << 24;
    return i + j;
}
